/**
 * 
 */
package mycsveditor.editors;

import org.eclipse.jface.text.TextAttribute;
import org.eclipse.jface.text.rules.BufferedRuleBasedScanner;
import org.eclipse.jface.text.rules.ICharacterScanner;
import org.eclipse.jface.text.rules.IToken;
import org.eclipse.jface.text.rules.RuleBasedScanner;
import org.eclipse.jface.text.rules.Token;
import org.eclipse.jface.text.source.ISharedTextColors;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.RGB;

/**
 * @author dongxu
 * 
 */
public class JavaCallScanner extends BufferedRuleBasedScanner {

	public static final int NOT_DEFINED = 0;
	public static final int CLASS = 1;
	public static final int METHOD = 2;
	public static final int PARA_NAME = 3;
	public static final int PARA_VAL = 4;

	private IToken[] fTokens;

	private ISharedTextColors fColorManager;

	private int fState;

	/**
	 * 
	 */
	public JavaCallScanner(ISharedTextColors colorManager) {
		super();
		init(colorManager);
	}

	/**
	 * @param size
	 */
	public JavaCallScanner(ISharedTextColors colorManager, int size) {
		super(size);
		init(colorManager);
	}

	private void init(ISharedTextColors colorManager) {
		fColorManager = colorManager;
		fTokens = new IToken[] {
				Token.UNDEFINED,
				createTokenByRGB(ICSVColorConstants.JAVA_CALL_CLASS),
				createToken(ICSVColorConstants.JAVA_CALL_METHOD, null, SWT.BOLD),
				createTokenByRGB(ICSVColorConstants.JAVA_CALL_PARA_NAME),
				createTokenByRGB(ICSVColorConstants.JAVA_CALL_PARA_VAL), };
	}

	private IToken createTokenByRGB(RGB rgb) {
		return createToken(rgb, null, SWT.NORMAL);
	}

	private IToken createToken(RGB foreground, RGB background, int style) {
		return new Token(new TextAttribute(fColorManager.getColor(foreground),
				background == null ? null : fColorManager.getColor(background),
				style));
	}

	@Override
	public IToken nextToken() {
		fTokenOffset = fOffset;
		fColumn = RuleBasedScanner.UNDEFINED;

		if (getColumn() == 0) {
			fState = CLASS;
		}

		int chr = read();
		
		if (chr == ',' && fState == PARA_VAL) {
			fState = PARA_NAME;
		}

		if (chr == ',' || chr == '=') {
			return fTokens[NOT_DEFINED];
		}
		if (chr == EOF) {
			return Token.EOF;
		}
		
		TokenScanner ts = TokenScanner.create(fState, this, chr);
		ts.scan();
		fState = ts.nextState();
		return fTokens[ts.token()];

/*===============================Below conditional code was replaced by polymorphism==========================================*/
//		if (fState == CLASS) {
//			while (chr != ',' && chr != EOF)
//				chr = read();
//			fState = METHOD;
//			unread();
//			return fTokens[CLASS];
//		}
//
//		if (fState == METHOD) {
//			while (chr != ',' && chr != EOF)
//				chr = read();
//			fState = PARA_NAME;
//			unread();
//			return fTokens[METHOD];
//		}
//
//		if (fState == PARA_NAME) {
//			while (chr != '=' && chr != EOF)
//				chr = read();
//			fState = PARA_VAL;
//			unread();
//			return fTokens[PARA_NAME];
//		}
//
//		if (fState == PARA_VAL) {
//			boolean quoted = false;
//			int prev = -1;
//			while ((chr != ',' || quoted) && chr != EOF) {
//				if (chr == '\"' && prev != '\\') {
//					quoted = !quoted;
//				}
//				prev = chr;
//				chr = read();
//			}
//			fState = PARA_NAME;
//			unread();
//			return fTokens[PARA_VAL];
//		}
//
//		return fTokens[fState];
	}

	public static class TokenScanner {

		protected ICharacterScanner scanner;

		protected int chr;
		
		protected int nextState;
		
		protected int token;

		public TokenScanner(ICharacterScanner scanner, int initChr) {
			this.scanner = scanner;
			chr = initChr;
			nextState = NOT_DEFINED;
			token = NOT_DEFINED;
		}

		public void scan() {
		}

		public void simpleScan(int stop) {
			while (chr != EOF && chr != stop) {
				chr = scanner.read();
			}
			scanner.unread();
		}

		public int nextState() {
			return nextState;
		}

		public int token() {
			return token;
		}

		public static TokenScanner create(int state, ICharacterScanner scanner,
				int initChr) {
			switch (state) {
			case CLASS:
				return new ClassScanner(scanner, initChr);
			case METHOD:
				return new MethodScanner(scanner, initChr);
			case PARA_NAME:
				return new ParaNameScanner(scanner, initChr);
			case PARA_VAL:
				return new ParaValScanner(scanner, initChr);
			default:
				return new TokenScanner(scanner, initChr);
			}
		}

	}

	public static class ClassScanner extends TokenScanner {

		public ClassScanner(ICharacterScanner scanner, int initChr) {
			super(scanner, initChr);
			nextState = METHOD;
			token = CLASS;
		}

		@Override
		public void scan() {
			simpleScan(',');
		}

	}

	public static class MethodScanner extends TokenScanner {

		public MethodScanner(ICharacterScanner scanner, int initChr) {
			super(scanner, initChr);
			nextState = PARA_NAME;
			token = METHOD;
		}

		@Override
		public void scan() {
			simpleScan(',');
		}

	}

	public static class ParaNameScanner extends TokenScanner {

		public ParaNameScanner(ICharacterScanner scanner, int initChr) {
			super(scanner, initChr);
			nextState = PARA_VAL;
			token = PARA_NAME;
		}

		@Override
		public void scan() {
			simpleScan('=');
		}

	}

	public static class ParaValScanner extends TokenScanner {

		public ParaValScanner(ICharacterScanner scanner, int initChr) {
			super(scanner, initChr);
			nextState = PARA_NAME;
			token = PARA_VAL;
		}

		@Override
		public void scan() {
			boolean quoted = false;
			int prev = -1;
			while ((chr != ',' || quoted) && chr != EOF) {
				if (chr == '\"' && prev != '\\') {
					quoted = !quoted;
				}
				prev = chr;
				chr = scanner.read();
			}
			scanner.unread();
		}

	}

}
